package edu.gatech.blackjackai.carddata;

import java.util.ArrayList;

/**
 * Deck encapsulates all of the data within a deck of cards. It keeps track of the cards currently contained within
 * a deck, as well as other data having to do with a deck of cards
 * @author Sean Gillespie
 * @version 1.0
 *
 */
public class Deck {
	private ArrayList<CardValue> cards;
	private final CardValue[] cardNumList = {CardValue.TWO, CardValue.THREE, CardValue.FOUR, CardValue.FIVE, CardValue.SIX, 
									 CardValue.SEVEN, CardValue.EIGHT, CardValue.NINE, 
									 CardValue.TEN, CardValue.J, CardValue.Q, CardValue.K, CardValue.A};
	/**
	 * Creates a new default Deck with 52*numDecks cards. This is used to initialize a deck to be drawn from.
	 * @param numDecks The number of decks that are being played with.
	 */
	public Deck(int numDecks) {
		if (numDecks <= 0)
			numDecks = 1;
		cards = new ArrayList<CardValue>();
		for (int i = 0; i < numDecks; i++) {
			for (int j = 0; j < 13; j++) {
				for (int k = 0; k < 4; k++) {
					cards.add(cardNumList[j]);
				}
			}
		}
	}
	
	/**
	 * Creates a new deck with zero cards in it. This is used to initialize discard and table decks.
	 */
	public Deck() {
		cards = new ArrayList<CardValue>();
	}
	
	/**
	 * removeCard removes a card from this deck. If the card is not found in the deck, this method returns null.
	 * @param card The value of the card to be removed.
	 * @return The card that was removed.
	 */
	public CardValue removeCard(CardValue card) {
		boolean removed = cards.remove(card);
		if (!removed)
			return null;
		return card;
	}
	
	/**
	 * addCard adds a card to this deck.
	 * @param card The card to be added to the deck
	 */
	public void addCard(CardValue card) {
		cards.add(card);
	}
	
	/**
	 * numCardType counts how many of a certain card are in a deck.
	 * @param card The card to count
	 * @return The number of cards equal to the parameter in the deck.
	 */
	public int numCardType(CardValue card) {
		int counter = 0;
		for (CardValue cardValue : cards) {
			if (cardValue == card)
				counter++;
		}
		return counter;
	}
	
	/**
	 * probDrawCard returns the probability that a certain card will be drawn next. 
	 * @param card The card whose probability is to be evaluated
	 * @return The probability that the card will be drawn next
	 */
	public double probDrawCard(CardValue card) {
		try {
			return numCardType(card) / (double)cards.size();
		} catch (ArithmeticException e) {
			return 0;
		}
	}
	
	/**
	 * clearDeck clears all of the cards from the deck.
	 */
	public void clearDeck() {
		cards.clear();
	}
	
	/**
	 * Adds all of the cards from the parameter deck to this deck.
	 * @param deck The deck whose cards will be added to this deck.
	 */
	public void addAll(Deck deck) {
		cards.addAll(deck.getCards());
	}
	
	/**
	 * Computes the expected value of the next card to be drawn from this deck. 
	 * @return The expected value of the next card to be drawn from this deck.
	 */
	public double deckExpectedValue() {
		double sum = 0;
		for (CardValue card : CardValue.values()) {
			sum = sum + (card.value() * probDrawCard(card));
		}
		return sum;
	}
	
	/**
	 * Returns the cards ArrayList.
	 * @return The cards ArrayList.
	 */
	public ArrayList<CardValue> getCards() {
		return cards;
	}
	
	/**
	 * Sets this deck's cards to the parameter value.
	 * @param cards The new cards ArrayList
	 */
	public void setCards(ArrayList<CardValue> cards) {
		this.cards = cards;
	}
	
	public int numCards() {
		return cards.size();
	}
	
}
